#include "comm_def.h"
#include "mz_getopt.h"
#include "mz_debug.h"

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

typedef struct _usr_def_t {
	MZ_ARG_INFO info;
	char long_param_name[64];
	char short_param_name[4];
} MZ_USR_DEF_T;

static MZ_USR_DEF_T _usr_def[32];
static unsigned int _usr_def_cnt = 0;

static int _build_single(MZ_USR_DEF_T* pud, char* str) {
	int i = 0;
	int st = 0; //0:long_param, 1:short_param, 2:get_type, 3:end
	int idx = 2;

	pud->long_param_name[0] = '-';
	pud->long_param_name[1] = '-';
	pud->short_param_name[0] = '-';
	while(str[i]) {
		if (0 == st) {
			if ('|' == str[i]) {
				idx = 1;
				st = 1;
			} else if ('=' == str[i]) {
				st = 2;
			} else if ('!' == str[i]) {
				st = 3;
				pud->info.param_type = MZ_NoParam;
			} else {
				pud->long_param_name[idx++] = str[i];
			}			
		} else if (1 == st) {
			if ('=' == str[i]) {
				st = 2;
			} else if ('!' == str[i]) {
				pud->info.param_type = MZ_NoParam;
			} else {
				pud->short_param_name[idx++] = str[i];
			}
		} else if (2 == st) {
			if ('s' == str[i]) {
				pud->info.param_type = MZ_String;
			} else if ('i' == str[i]) {
				pud->info.param_type = MZ_Int;
			} if ('f' == str[i]) {
				pud->info.param_type = MZ_Double;
			} else {
				return -1;
			} 
		}
		++i;
	}
	return 0;
}

static int _build_user_def_table(char** udarr) {
	_usr_def_cnt = 0;
	while(udarr[_usr_def_cnt]) {
		_build_single(&_usr_def[_usr_def_cnt], udarr[_usr_def_cnt]);
		++_usr_def_cnt;
		if (_usr_def_cnt >= sizeof(_usr_def) / sizeof(MZ_USR_DEF_T)) {
			printf("too many opts.\n");
			return -1;
		}
	}
	return 0;
}

static int _set_opt(char* curopt, unsigned int* pidx) {
	unsigned int i = 0;
	int len = strlen(curopt);
	for (; i < _usr_def_cnt; ++i) {
		if (0 == strcmp(_usr_def[i].long_param_name, curopt) || 0 == strcmp(_usr_def[i].short_param_name, curopt)) { //--longopt -shortopt
			*pidx = i;
			_usr_def[i].info.recved = MZ_OPT_FOUND;
			return 1;//success
		}
	}
	return -1;
}

static int _add_opt(char* popt, unsigned int idx) {
	if ((unsigned int)-1 == idx) {
		return -1;
	}

	if (MZ_NoParam == _usr_def[idx].info.param_type) {
		return -1;
	}

	if (0 == _usr_def[idx].info.param_arr) { //first param
		switch(_usr_def[idx].info.param_type) {
			case MZ_Int:
			case MZ_Boolean:
				_usr_def[idx].info.param_arr = (int*)malloc(sizeof(int));
				((int*)_usr_def[idx].info.param_arr)[0] = atoi(popt);
				_usr_def[idx].info.param_cnt = 1;
				break;
			case MZ_Double:
				_usr_def[idx].info.param_arr = (int*)malloc(sizeof(double));
				((double*)_usr_def[idx].info.param_arr)[0] = atof(popt);
				_usr_def[idx].info.param_cnt = 1;
				break;
			case MZ_String:
				_usr_def[idx].info.param_arr = (char*)malloc(sizeof(char*));
				((char**)_usr_def[idx].info.param_arr)[0] = (char*)malloc(strlen(popt) + 1);
				strncpy(((char**)_usr_def[idx].info.param_arr)[0], popt, strlen(popt)+1);
				_usr_def[idx].info.param_cnt = 1;
				break;
			default:
				break;
		}
	} else {
		switch(_usr_def[idx].info.param_type) {
			case MZ_Int:
			case MZ_Boolean:
				_usr_def[idx].info.param_arr = (int*)realloc(_usr_def[idx].info.param_arr, sizeof(int)*(_usr_def[idx].info.param_cnt + 1));
				((int*)_usr_def[idx].info.param_arr)[_usr_def[idx].info.param_cnt] = atoi(popt);
				_usr_def[idx].info.param_cnt += 1;
				break;
			case MZ_Double:
				_usr_def[idx].info.param_arr = (int*)realloc(_usr_def[idx].info.param_arr, sizeof(double) * (_usr_def[idx].info.param_cnt + 1));
				((double*)_usr_def[idx].info.param_arr)[_usr_def[idx].info.param_cnt] = atof(popt);
				_usr_def[idx].info.param_cnt += 1;
				break;
			case MZ_String:
				_usr_def[idx].info.param_arr = (char*)realloc(_usr_def[idx].info.param_arr, sizeof(char*) * (_usr_def[idx].info.param_cnt + 1));
				((char**)_usr_def[idx].info.param_arr)[_usr_def[idx].info.param_cnt] = (char*)malloc(strlen(popt) + 1);
				strncpy(((char**)_usr_def[idx].info.param_arr)[_usr_def[idx].info.param_cnt], popt, strlen(popt)+1);
				_usr_def[idx].info.param_cnt += 1;
				break;
			default:
				break;
		}
	}
	return 0;
}

int mz_make_opt_result(int argc, char** argv, char** udarr) {
	int i = 0, j = 0;
	unsigned int cur_idx = (unsigned int)-1;

	_build_user_def_table(udarr);
	for (i = 1; i < argc; ++i) {
		if (1 == _set_opt(argv[i], &cur_idx)) {
		} else {
			_add_opt(argv[i], cur_idx);
		}
	}
	return 0;
}

int mz_having_arg(char* arg) {
	unsigned int i = 0; 
	for (; i < _usr_def_cnt; ++i) {
		//jump -- symbol beginning
		if (0 == strcmp(_usr_def[i].long_param_name + 2, arg) || 0 == strcmp(_usr_def[i].short_param_name + 1, arg)) {
			return _usr_def[i].info.recved;
		}
	}
	return MZ_OPT_NO_FOUND;
}


MZ_ARG_INFO* get_arg_result(char* key) {
	unsigned int i = 0;
	for (; i < _usr_def_cnt; ++i) {
		if (0 == strcmp(_usr_def[i].long_param_name + 2, key) || 0 == strcmp(_usr_def[i].short_param_name + 1, key)) {
			return &_usr_def[i].info;
		}
	}
	return 0;
}

int mz_release_opt_rs() {
	unsigned int i = 0;
	unsigned int j = 0;
	for (; i < _usr_def_cnt; ++i) {
		if (_usr_def[i].info.param_arr) {
			for (; j < _usr_def[i].info.param_cnt; ++j) {
				free((void*)((int*)_usr_def[i].info.param_arr)[j]);
			}	
		}
	}
	return 0;
}